{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Copying Sequences"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Shallow Copies"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Simple Loop"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Really not a very Pythonic approach, but it works..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "\n",
    "l1_copy = []\n",
    "for item in l1:\n",
    "    l1_copy.append(item)\n",
    "\n",
    "print(l1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can see that `l1` and `l1_copy` are not the same objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 is l1_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### List Comprehension"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use a list comprehension to do exactly what we did in the previous example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "l1_copy = [item for item in l1]\n",
    "print(l1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And once again, the objects are not the same:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 is l1_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Using the copy() method"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since lists are mutable sequence types, they have the `copy()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "l1_copy = l1.copy()\n",
    "print(l1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And once again, the objects are different:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 is l1_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Using the built-in list() Function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The built-in `list()` function will make a list out of any iterable. This always ends up with a copy of the iterable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l1 = [1, 2, 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "l1_copy = list(l1)\n",
    "print(l1_copy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 is l1_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that `list()` will take in any iterable, so you can technically copy any iterable into a list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "t1 = (1, 2, 3)\n",
    "t1_copy = list(t1)\n",
    "print(t1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, we get a list, not a tuple - so not exactly a copy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We've seen this before, but be careful with the `tuple()` built-in function. When we copy tuples, since they are immutable, we just get the original tuple back:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3)\n"
     ]
    }
   ],
   "source": [
    "t1 = (1, 2, 3)\n",
    "t1_copy = tuple(t1)\n",
    "print(t1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But here, the objects are the **same**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t1 is t1_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Using Slicing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also use slicing to copy sequences.\n",
    "\n",
    "We'll cover slicing in detail in an upcoming lecture, but with slicing we can also access subsets of the sequence - here we use slicing to select the entire sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "l1_copy = l1[:]\n",
    "print(l1_copy)\n",
    "print(l1 is l1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But again, watch out with tuples!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3)\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "t1 = (1, 2, 3)\n",
    "t1_copy = t1[:]\n",
    "print(t1_copy)\n",
    "print(t1 is t1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, since the slice was the entire tuple, a copy was not made, instead the reference to the original tuple was returned!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Same deal with strings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "python\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "s1 = 'python'\n",
    "s2 = str(s1)\n",
    "print(s2)\n",
    "print(s1 is s2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "python\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "s1 = 'python'\n",
    "s2 = s1[:]\n",
    "print(s2)\n",
    "print(s1 is s2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you're wondering why Python has that behavior, just think about it.\n",
    "\n",
    "If you create a copy of a tuple, what are you going to do to that copy? Modify it?? You can't!\n",
    "\n",
    "Modify the contents of a contained mutable element? Sure you can, but whether you had a copy or not, you would still be modifying the **same** element - having the sequence copied is no safer than not.\n",
    "\n",
    "Not needed, so Python basically optimizes things for us."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### The `copy` module"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `copy` module has a generic `copy` function as well:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "l1_copy = copy.copy(l1)\n",
    "print(l1_copy)\n",
    "print(l1 is l1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And for tuples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3)\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "t1 = (1, 2, 3)\n",
    "t1_copy = copy.copy(t1)\n",
    "print(t1_copy)\n",
    "print(t1 is t1_copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see the same thing happens with tuples as we saw before."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Shallow vs Deep Copies"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What we have been doing so far is creating **shallow** copies.\n",
    "\n",
    "This means that when a sequence is copied, each element of the new sequence is bound to precisely the same memory address as the corresponding element in the original sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "v1 = [0, 0]\n",
    "v2 = [0, 0]\n",
    "\n",
    "line1 = [v1, v2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0, 0], [0, 0]]\n",
      "2018950765896 2018950764104\n"
     ]
    }
   ],
   "source": [
    "print(line1)\n",
    "print(id(line1[0]), id(line1[1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's make a copy of the line using any of the techniques we just looked at:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "line2 = line1.copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "line1 is line2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So not the same objects. Now let's look at the contained elements themselves:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2018950765896 2018950764104\n",
      "2018950765896 2018950764104\n"
     ]
    }
   ],
   "source": [
    "print(id(line1[0]), id(line1[1]))\n",
    "print(id(line2[0]), id(line2[1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the element references are the same!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, if we do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "line2[0][0] = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[100, 0], [0, 0]]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "line2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[100, 0], [0, 0]]"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "line1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`line1`'s contents has also changed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want the contained elements **also** to be copied, then we need to explicitly do so as well. This is called creating a **deep** copy.\n",
    "\n",
    "Let's see how we might do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "v1 = [0, 0]\n",
    "v2 = [0, 0]\n",
    "\n",
    "line1 = [v1, v2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "line2 = [item[:] for item in line1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2018950613000 2018948315720\n",
      "2018950712904 2018950766408\n"
     ]
    }
   ],
   "source": [
    "print(id(line1[0]), id(line1[1]))\n",
    "print(id(line2[0]), id(line2[1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, now we have copies of the elements as well:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[100, 0], [0, 0]]\n",
      "[[0, 0], [0, 0]]\n"
     ]
    }
   ],
   "source": [
    "line1[0][0] = 100\n",
    "print(line1)\n",
    "print(line2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and `line2` is unaffacted when we modify `line1`.\n",
    "\n",
    "So not only did we do a copy of `line1`, but we also made a shallow copy of `v1` and `v2` as well.\n",
    "\n",
    "But the problem is that we only went two levels deep - what if the variables `v1` and `v2` themselves contained mutable types instead of just integers? We would have to nest deeper and deeper - in general that's what a deep copy needs to do, and usually recursive approaches need to be used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fortunately, Python has that functionality built-in for us so we don't have to do that!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `copy` module has a `deepcopy()` function we can use to create deep copies. It handles all kinds of weird situations where we might have circular references - doing it ourselves is certainly possible, but does take some work."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "v1 = [0, 0]\n",
    "v2 = [0, 0]\n",
    "line1 = [v1, v2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2018950611912 2018948484808\n",
      "2018950458184 2018950611976\n"
     ]
    }
   ],
   "source": [
    "line2 = copy.deepcopy(line1)\n",
    "print(id(line1[0]), id(line1[1]))\n",
    "print(id(line2[0]), id(line2[1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "line2[0][0] = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0, 0], [0, 0]]\n",
      "[[100, 0], [0, 0]]\n"
     ]
    }
   ],
   "source": [
    "print(line1)\n",
    "print(line2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And of course, it works with any level of nested objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[[11, 12], [21, 22]], [[31, 32], [41, 42]]]\n"
     ]
    }
   ],
   "source": [
    "v1 = [11, 12]\n",
    "v2 = [21, 22]\n",
    "line1 = [v1, v2]\n",
    "\n",
    "v3 = [31, 32]\n",
    "v4 = [41, 42]\n",
    "line2 = [v3, v4]\n",
    "\n",
    "plane1 = [line1, line2]\n",
    "print(plane1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "plane2 = copy.deepcopy(plane1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[[11, 12], [21, 22]], [[31, 32], [41, 42]]]\n"
     ]
    }
   ],
   "source": [
    "print(plane2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[11, 12], [21, 22]] 2018950458632\n",
      "[[11, 12], [21, 22]] 2018950611080\n"
     ]
    }
   ],
   "source": [
    "print(plane1[0], id(plane1[0]))\n",
    "print(plane2[0], id(plane2[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[11, 12] 2018948481288\n",
      "[11, 12] 2018950763080\n"
     ]
    }
   ],
   "source": [
    "print(plane1[0][0], id(plane1[0][0]))\n",
    "print(plane2[0][0], id(plane2[0][0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Even works with custom classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return f'Point({self.x}, {self.y})'\n",
    "    \n",
    "class Line:\n",
    "    def __init__(self, p1, p2):\n",
    "        self.p1 = p1\n",
    "        self.p2 = p2\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Line({self.p1.__repr__()}, {self.p2.__repr__()})'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Point(0, 0) 2018950806944\n",
      "Point(0, 0) 2018950807280\n"
     ]
    }
   ],
   "source": [
    "p1 = Point(0, 0)\n",
    "p2 = Point(10, 10)\n",
    "line1 = Line(p1, p2)\n",
    "line2 = copy.deepcopy(line1)\n",
    "\n",
    "print(line1.p1, id(line1.p1))\n",
    "print(line2.p1, id(line2.p1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the memory address of the points are different - that was because of the deep copy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, if we had done a shallow copy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Point(0, 0) 2018950806832\n",
      "Point(0, 0) 2018950806832\n"
     ]
    }
   ],
   "source": [
    "p1 = Point(0, 0)\n",
    "p2 = Point(10, 10)\n",
    "line1 = Line(p1, p2)\n",
    "line2 = copy.copy(line1)\n",
    "\n",
    "print(line1.p1, id(line1.p1))\n",
    "print(line2.p1, id(line2.p1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the memory address of the points are now the **same**."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
